#include "Scene.h"
#include "Memory.h"
#include "Vec3.h"
#include "Plane.h"
#include "Screen.h"
#include "Camera.h"
#include <SDL_opengl.h>
#include "EventPoller.h"
#include "Triangle.h"
#include "TaskBasedThread.h"
#include "CriticalSection.h"
#include "RayTraceThread.h"
#include "Grid.h"
#include <cassert>
#include "AABBox.h"
#include "Disk.h"
#include "Trace_All.h"
#include "Texture.h"
#include "MeshGrid.h"
#include "Debug.h"
#include "MainRenderState.h"

/* Construction and Destruction */
Scene::Scene(void):
m_rayTracer(NULL),
m_clearColor(0,0,0),
m_mainArray(0),
m_depthBuffer(0),
m_camera(NULL),
m_triTex(NULL),
m_grid(NULL),
m_resX(0),
m_resY(0),
m_threadFinishCounter(0),
m_renderDepth(false),
m_threadEndCounter(0),
m_numThreads(0),
m_finishEvent(NULL),
m_endEvent(NULL),
m_threadTaskCS(NULL),
m_threadEndCS(NULL),
m_threadFinishCS(NULL),
m_pixelSize(0),
m_numTasksLeft(-1)
{
    for(int i = 0; i < 8; ++i)
        m_threads[i] = NULL;
    for(int i = 0; i < 8; ++i)
        m_taskTimers[i] = 0.0;
}
Scene::~Scene()
{
    CleanUp();
}
static float angle = 0.0f;
/* The main render functions */
void Scene::RenderSingleThreaded(void)
{
    /* Create the necessary variables */
    Color	    finalColor;	 	
    Ray			ray;					
    ray.m_origin = (m_camera->GetPos());

    //for each sample
    for (int y = 0; y < m_resY; ++y)
    {
        for (int x = 0; x < m_resX; ++x) 
        {			
            m_camera->ComputeDirection(x,y, &ray.m_dir, this);
            //Trace it
            finalColor = m_rayTracer->TraceRay(ray);
            SetPixelColor(x, y, finalColor);
        }
    }
}  
void Scene::RenderMultipleThreaded(void)
{
    m_camera->SetPosition(m_camera->GetPos() +m_cameraMove);
    m_camera->ComputeONB();
    //RenderSingleThreaded();
    //RenderWithSingleThread();
    //RenderWithTwoThreads();
    //RenderWithFourThreads();
    for(int i = 0; i < 8; ++i)
        m_taskTimers[i] = 0.0;
    RenderWithNThreads();

    //Render the pixels
    GLRenderPixels();
}  
/* Platform independent implementation */
void Scene::SetPixelColor(const int row, const int column, const Color& raw_color)
{
    //Call the openGL version
    GLSetPixel(row, column, raw_color.m_r,raw_color.m_g,raw_color.m_b);
}
bool Scene::TrySetDepth( int row, int column, float dist )
{
    float currentValue = m_depthBuffer[(row)   + (column * m_resY)];
    if(/*dist < currentValue &&*/ dist < m_camera->GetFar() && dist > m_camera->GetNear())
    {
        m_depthBuffer[(row)   + (column * m_resY)] = (m_camera->GetFar()-dist) / m_camera->GetFar();
        return true;
    }
    return false;
}
/* Check this ray against the scene */
ContactData Scene::CheckContact(const Ray& ray, bool nearest) 
{

    ContactData ret(this);		
    CheckContactsGrid(ret, ray, nearest);
    //CheckContactsBruteForce(ret, ray, nearest);
    return ret;
}
/* Tidy up memory */
void Scene::CleanUp() 
{
    TheEventPoller::Instance()->RemoveHandler(this);
    TheEventPoller::Instance()->RemoveHandler(m_camera);

    //End the worker threads neatly
    if(m_threadTaskCS)
    {
        ScopedCriticalSection s(*m_threadTaskCS);
        m_numTasksLeft = -1;
    }
    for(int i = 0; i < m_numThreads; ++i)
    {
        m_threads[i]->WaitForFinish();
    }

    for(int i = 0; i < m_numThreads; ++i)
    {
        delete m_threads[i];
    }
    m_numThreads = 0;
    TidyMutexs();
    //delete all the lights
    for(unsigned int i = 0; i < m_lights.size(); ++i)
    {
        delete m_lights[i];
    }
    m_lights.clear();

    //delete the ray tracer
    if(m_rayTracer) 
    {
        delete m_rayTracer;
        m_rayTracer = NULL;
    }	

    //Call delete on all objects, and then clear the vector as well
    int num_objects = m_renderableObjects.size();
    for (int index = 0; index < num_objects; ++index) 
    {
        delete m_renderableObjects[index];
        m_renderableObjects[index] = NULL;
    }	
    m_renderableObjects.erase (m_renderableObjects.begin(), m_renderableObjects.end());

    //Cleanup the main array && depth buffer
    if(m_mainArray)
        delete[] m_mainArray;
    m_mainArray = NULL;

    if(m_depthBuffer)
        delete[] m_depthBuffer;
    m_depthBuffer = NULL;

    //get rid of the camera
    if(m_camera)
        delete m_camera;
    m_camera = NULL;

    if(m_grid)
    {
        m_grid->CleanUp();
        delete m_grid;
    }
    m_grid = NULL;

    if(m_triTex)
    {
        m_triTex->CleanUp();
        delete m_triTex;
    }
    m_triTex = NULL;

    m_numTasksLeft = -1;
    m_tasks.clear();
}
#include "DebugTracer.h"
void Scene::SubdivideTaskList()
{
    int current  = m_tasks.size();
    for(int i = 0; i < current; ++i)
    {
        Ray_Trace_Task tarray[4];
        SplitTaskInto4(m_tasks[i], tarray);
        for(int c = 0; c < 4; ++c)
            m_tasks.push_back(tarray[c]);
    }
    //erase the first batch...dont render it all twice! lol
    m_tasks.erase(m_tasks.begin(), m_tasks.begin() + current);
    m_numTasksLeft = m_tasks.size() - 1;
}
void Scene::Build(MainRenderStateData& data) 
{
    if(!m_finishEvent)
    {
        m_finishEvent = CreateEvent(0,0,0,0);
        ResetEvent(m_finishEvent);
    }
    if(!m_endEvent)
    {
        m_endEvent = CreateEvent(0,0,0,0);
        ResetEvent((m_endEvent));
    }
        
    m_threadFinishCS = new CriticalSection;
    m_threadTaskCS = new CriticalSection;
    m_threadEndCS = new CriticalSection;
    for(int i = 0; i < 8; ++i)
        m_threads[i] = NULL;

    for(int i = 0; i < 8; ++i)
        m_taskTimers[i] = 0.0;

    //Set up the sample sizes
    m_resY = TheScreen::Instance()->GetHeight();
    m_resX = TheScreen::Instance()->GetWidth();
    m_pixelSize = 2.0f;
    m_numThreads = data.numThreads;
    //make the tasks
    m_tasks.push_back(Ray_Trace_Task(0, m_resX,0,m_resY, 1.0f, 1.0f, 1.0f));
    if(data.taskSplit == 0)
        for(int i = 0; i < data.taskDivision; ++i)
            SubdivideTaskList();
    //assign enough memory to store the data
    m_mainArray = new float[m_resX * m_resY * 3];
    memset(m_mainArray, 0, sizeof(float)*(m_resX * m_resY * 3));
    m_depthBuffer = new float[m_resX * m_resY];
    memset(m_depthBuffer,0, sizeof(float)*(m_resX * m_resY));
    //Create our virtual implementation
    m_rayTracer = new TraceAll(this, data.recursion);
    //m_rayTracer = new DebugTracer(this);
    //Different types of camera go here...
    m_camera = new Camera;

    //Set our clear color up, make sure we can handle events   
    m_clearColor.SetBlack();
    TheEventPoller::Instance()->AddHandler(this);

    TheEventPoller::Instance()->AddHandler(m_camera);

    //Create a new Grid
    m_grid = new Grid(*this, m_renderableObjects);
    
    switch(data.whichBuild)
    {
    case 0 :
        {
            BuildSimpleProcedrual(data);
            break;
        }
    case 1 :
        {
            BuildComplexProcedrual(data);
            break;
        }
    case 2 :
        {
            UseSimpleMesh(data);
            break;
        }
    case 3:
        {
            UseCityMesh(data);
            break;
        }
    }   
    
  
    /************************************************************************/
    /* Generate the Grid                                                    */
    /************************************************************************/
    m_grid->SetUpCells();

    //create the threads
    float r[8] = {1.1f,1.8f,1.5f,1.5f,1.5f,1.9f,1.1f,1.9f};
    float g[8] = {1.1f,1.5f,1.8f,1.5f,1.5f,1.9f,1.9f,1.1f};
    float b[8] = {1.1f,1.5f,1.5f,1.8f,1.5f,1.1f,1.9f,1.9f};
    for(int i = 0; i < m_numThreads; ++i)
    {
        TaskBasedThread* t = new TaskBasedThread(this, m_rayTracer, m_camera );
        t->SetID(i);
        t->SetWhichMask(data.tileMode); //0 = none, 1 = personal, 2 = task
        t->SetColor(Color(r[i],g[i],b[i]));
        m_threads[i] = t;
    }
    m_numTasksLeft = m_tasks.size() - 1;
    for(int i = 0; i < m_numThreads; ++i)
    {
        m_threads[i]->Start();
    }

}
/* OpenGl version uses an array of data, and then blitz' it all across as one rectangle */
void Scene::GLSetPixel(int x, int y, float r, float g, float b)
{
    float max = std::max(r, std::max(g,b));
    if(max > 1)
    {
        r = r / max;
        g = g / max;
        b = b / max;
    }
    {
        m_mainArray[(x *3)   + (y * m_resY * 3)] = r;
        m_mainArray[(x *3)+1 + (y * m_resY * 3)] = g;
        m_mainArray[(x *3)+2 + (y * m_resY * 3)] = b;
    }
}
void Scene::GLRenderPixels()
{
    if(m_renderDepth)
        glDrawPixels(m_resX,m_resY, GL_BLUE, GL_FLOAT, m_depthBuffer);
    else
        glDrawPixels(m_resX,m_resY, GL_RGB, GL_FLOAT, m_mainArray);
}
void Scene::OnKeyBoardDownEvent(const SDL_KeyboardEvent& Event)
{
    if(Event.keysym.sym == SDLK_LEFT)
    {
        m_pixelSize *= 0.5f;  
    }
    else if(Event.keysym.sym == SDLK_RIGHT)
    {
        m_pixelSize *= 2;  
    }
    else if(Event.keysym.sym == SDLK_s)
    {
        m_renderDepth = !m_renderDepth;
    }
    else if(Event.keysym.sym == SDLK_a)
    {
        for(unsigned int i = 0; i < m_tasks.size(); ++i)
            m_tasks[i].SetMask(1,1,1);
    }
}
void Scene::RenderWithFourThreads()
{
    //create the threads
    Thread* threads[4];
    //top left
    threads[0] = new RayTraceThread(this, m_rayTracer, m_camera, 0, 256,0,256);
    //top right
    threads[1] = new RayTraceThread(this, m_rayTracer, m_camera, 256, 512,0,256);
    //bottom left
    threads[2] = new RayTraceThread(this, m_rayTracer, m_camera, 0, 256,256,512);
    //bottom right
    threads[3] = new RayTraceThread(this, m_rayTracer, m_camera, 256, 512,256,512);

    for(int i = 0; i < 4; ++i)
    {
        threads[i]->Start();
    }
    //now we wait for them to finish
    int retVal[4] = {0,0,0,0};
    for(int i = 0; i < 4; ++i)
    {
        retVal[i] = threads[i]->WaitForFinish();
        delete threads[i];
    }
    if(retVal[0] != 1 || retVal[1] != 1 || retVal[2] != 1 || retVal[3] != 1)
        assert(0);
}
void Scene::RenderWithNThreads()
{
    //Start the Threads
    for(int i = 0; i < m_numThreads; ++i)
    {
        m_threads[i]->vResume();
    }
    //wait for em
    WaitForSingleObject(m_finishEvent, INFINITE);
    //They are done now, so tell them to sleep again
    for(int i = 0; i < m_numThreads; ++i)
    {
        m_threads[i]->vPause();
    }
}
void Scene::RenderWithSingleThread()
{
    //create the threads
    Thread* threads[1];
    //left
    threads[0] = new RayTraceThread(this, m_rayTracer, m_camera, 0, 512,0,512);

    for(int i = 0; i < 1; ++i)
    {
        threads[i]->Start();
    }
    //now we wait for them to finish
    int retVal[1] = {0};
    for(int i = 0; i < 1; ++i)
    {
        retVal[i] = threads[i]->WaitForFinish();
        delete threads[i];
    }
    if(retVal[0] != 1 )
        assert(0);
}
void Scene::RenderWithTwoThreads()
{
    //create the threads
    Thread* threads[2];
    //left
    threads[0] = new RayTraceThread(this, m_rayTracer, m_camera, 0, 256,0,512);
    //right
    threads[1] = new RayTraceThread(this, m_rayTracer, m_camera, 256, 512,0,512);

    for(int i = 0; i < 2; ++i)
    {
        threads[i]->Start();
    }
    //now we wait for them to finish
    int retVal[2] = {0,0};
    for(int i = 0; i < 2; ++i)
    {
        retVal[i] = threads[i]->WaitForFinish();
        delete threads[i];
    }
    if(retVal[0] != 1 || retVal[1] != 1)
        assert(0);
}
std::vector<Light*>& Scene::GetLights()
{
    return m_lights;
}
vRenderables& Scene::GetRenderables()
{
    return m_renderableObjects;
}
void Scene::CheckContactsGrid( ContactData& ret, const Ray& ray,bool nearest )
{
    float tmin = FLT_MAX;
    m_grid->Hit(ray, tmin, ret);
}
void Scene::CheckContactsBruteForce( ContactData& ret, const Ray& ray,bool nearest )
{
    ContactData temp(this);
    float		t = 0.0f; 			
    float		tmin 			= FLT_MAX; // a MASSIVE number
    int 		num_objects 	= m_renderableObjects.size();

    for (int j = 0; j < num_objects; ++j)
        if ((ray.m_hitObject != m_renderableObjects[j]) && //if this ray has not already been checked against this
            m_renderableObjects[j]->Hit(ray, t, temp) && (t < tmin)) 
        {
            ret.m_hitTarget = temp.m_hitTarget;
            ret.m_hitNormal = temp.m_hitNormal;
            ret.m_hitPoint = temp.m_hitPoint;
            ret.m_hit	        = true;
            if(temp.HasColorStorred())
                ret.SetColor(temp.GetColor());
            tmin 				= t; 
            //if we dont need the nearest object, get out of here!
            if(!nearest)
                return;
        }
}
void Scene::ThreadFinished()
{
    {
        ScopedCriticalSection s(*m_threadFinishCS);
        m_threadFinishCounter++;
    }
    if(m_threadFinishCounter == m_numThreads)
    {
        SetEvent(m_finishEvent);
        m_threadFinishCounter = 0;
        m_numTasksLeft = m_tasks.size() - 1;
    }
}
bool Scene::GetTask( Ray_Trace_Task& t )
{
    int taskIndex = 0;
    {
        ScopedCriticalSection s(*m_threadTaskCS);
        taskIndex = m_numTasksLeft;
        m_numTasksLeft--;
    }
    if(taskIndex < 0)
    {
        return false;
    }
    else
    {
        t = m_tasks[taskIndex];
        return true;
    }
}
void Scene::ThreadEnded()
{
    {
        ScopedCriticalSection s(*m_threadEndCS);
        m_threadEndCounter++;
    }
    if(m_threadEndCounter == m_numThreads)
    {
        SetEvent(m_endEvent);
    }
}
void Scene::TidyMutexs()
{
    //clean up all mutexs
    if(m_threadEndCS)
    {
        m_threadEndCS->Unlock();
        delete m_threadEndCS;
    }
    m_threadEndCS = NULL;

    if(m_threadTaskCS)
    {
        m_threadTaskCS->Unlock();
        delete m_threadTaskCS;
    }
    m_threadTaskCS = NULL;

    if(m_threadFinishCS)
    {
        m_threadFinishCS->Unlock();
        delete m_threadFinishCS;
    }
    m_threadFinishCS = NULL;
}
void Scene::UseCityMesh(MainRenderStateData& data)
{
    Material m;
    m.SetAmbient(Color(0.8f,0.8f,0.8f,1.0f));
    m.SetDiffuse(Color(0.8f,0.8f,0.8f,1.0f));
    m.SetSpecular(Color(1.0f,1.0f,1.0f,1.0f));

    /************************************************************************/
    /* Making The City                                                     */
    /************************************************************************/
    MeshGrid* grid = new MeshGrid(*this, m, false, 2.0f, false, 0.001f);
    if(!grid->CreateFromOBJ("Meshes/city.obj", Vec3(800,800,800)))
        assert(0);
    grid->SetColor(1.0f,1.0f,1.0f);
    //grid->SetTexture(m_triTex);
    grid->GenerateGrid(data.smoothShading, true);
    AddRenderable(grid);

    //set up the camera stuff for wandering around the city
    m_cameraStart = Vec3(100, 100, 100);
    m_camera->SetPosition(m_cameraStart);
    m_camera->SetLook(0, 0, 0);
    m_camera->SetUp(0,1,0);
    m_camera->ComputeONB();
    m_camera->SetNearFar(0.5f, 1000.0f);
    m_cameraMove.SetStats(12,-1,20);
    //lighting

    /* Lighting */
    Vec3 arrPos[4];
    arrPos[0] = Vec3(90,110,90);
    arrPos[1] = Vec3(-100,110,-100);
    arrPos[2] = Vec3(100,110,-100);
    arrPos[3] = Vec3(-100,110,100);
    Vec3 arrDir[4];
    arrDir[0] = Vec3(-1,-1,-1);
    arrDir[1] = Vec3(1,-1,1);
    arrDir[2] = Vec3(-1,-1,1);
    arrDir[3] = Vec3(1,-1,-1);
    Vec3 arrCol[4];
    arrCol[0] = Vec3(1,1,1);
    arrCol[1] = Vec3(0,0,1);
    arrCol[2] = Vec3(0,1,0);
    arrCol[3] = Vec3(1,1,0);
    for( int i = 0; i < data.numLights; ++i)
    {
        Light* l = new Light(Color(arrCol[i]*0.8f),/* amb */
            Color(arrCol[i]*0.7f), /* dif */ 
            Color(arrCol[i]*1.2f), /* spec */2.0f, !data.pointLights, data.pointLights, arrDir[i], arrPos[i], 1.1f);
        m_lights.push_back(l);
        if(data.pointLights)
        {
            Material m;
            Color c(arrCol[i]);
            m.SetAmbient(c);
            m.SetSpecular(c);
            m.SetDiffuse(c);
            Sphere* s = new Sphere(arrPos[i], 5.0f, m,false,0.0f, false, 0.0f );
            s->SetColor(c);
            s->MakeLight();
            AddRenderable(s);
        }
    }
}
void Scene::UseSimpleMesh(MainRenderStateData& data)
{
    Material m;
    m.SetAmbient(Color(0.8f,0.8f,0.8f,1.0f));
    m.SetDiffuse(Color(0.8f,0.8f,0.8f,1.0f));
    m.SetSpecular(Color(1.0f,1.0f,1.0f,1.0f));

    /************************************************************************/
    /* Making The Scene                                                     */
    /************************************************************************/
    m_triTex = new Texture;
    if(!m_triTex->LoadFromFile("Textures/car.jpg"))
        m_triTex->LoadFromFile("Textures/default.png");
    MeshGrid* grid = new MeshGrid(*this, m, false, 2.0f, false, 0.001f);
    if(!grid->CreateFromOBJ("Meshes/car.obj", Vec3(1,1,1)))
        assert(0);
    grid->SetColor(1.0f,1.0f,1.0f);
    grid->SetTexture(m_triTex);
    grid->GenerateGrid(data.smoothShading, true);
    AddRenderable(grid);
    //set up the camera stuff for wandering around the city
    m_cameraStart = Vec3(30,30,30);
    m_camera->SetPosition(m_cameraStart);
    m_camera->SetLook(0, 0, 0);
    m_camera->SetUp(0,1,0);
    m_camera->ComputeONB();
    m_camera->SetNearFar(0.5f, 1000.0f);
    m_cameraMove.SetStats(-2.3f,-0.5f,-1.9f);

    /* Lighting */
    Vec3 arrPos[4];
    arrPos[0] = Vec3(30,30,30);
    arrPos[1] = Vec3(-30,30,-30);
    arrPos[2] = Vec3(30,30,-30);
    arrPos[3] = Vec3(-30,30,30);
    Vec3 arrDir[4];
    arrDir[0] = Vec3(-1,-1,-1);
    arrDir[1] = Vec3(1,-1,1);
    arrDir[2] = Vec3(-1,-1,1);
    arrDir[3] = Vec3(1,-1,-1);
    Vec3 arrCol[4];
    arrCol[0] = Vec3(1,1,1);
    arrCol[1] = Vec3(0,0,1);
    arrCol[2] = Vec3(0,1,0);
    arrCol[3] = Vec3(1,1,0);
    for( int i = 0; i < data.numLights; ++i)
    {
        Light* l = new Light(Color(arrCol[i]*0.8f),/* amb */
            Color(arrCol[i]*0.7f), /* dif */ 
            Color(arrCol[i]*1.2f), /* spec */2.0f, !data.pointLights, data.pointLights, arrDir[i], arrPos[i], 1.1f);
        m_lights.push_back(l);
        if(data.pointLights)
        {
            Material m;
            Color c(arrCol[i]);
            m.SetAmbient(c);
            m.SetSpecular(c);
            m.SetDiffuse(c);
            Sphere* s = new Sphere(arrPos[i], 1.0f, m,false,0.0f, false, 0.0f );
            s->SetColor(c);
            s->MakeLight();
            AddRenderable(s);
        }
    }
}
void Scene::BuildSimpleProcedrual(MainRenderStateData& data)
{
    Material m;
    m.SetAmbient(Color(0.8f,0.8f,0.8f,1.0f));
    m.SetDiffuse(Color(0.8f,0.8f,0.8f,1.0f));
    m.SetSpecular(Color(1.0f,1.0f,1.0f,1.0f));

    Sphere* s = new Sphere(Vec3(0,10,0), 20.0f, m, true, 0.8f, false, 0.3f);
    s->SetColor(1,0,0);
    AddRenderable(s);

    Plane* p = new Plane(Vec3(0,1,0), -20.0f,m,true,1.0f );
    p->SetColor(0,1,0);
    AddRenderable(p);

    //set up the camera stuff
    m_cameraStart = Vec3(30,30,30);
    m_camera->SetPosition(m_cameraStart);
    m_camera->SetLook(0, 0, 0);
    m_camera->SetUp(0,1,0);
    m_camera->ComputeONB();
    m_camera->SetNearFar(0.5f, 1000.0f);
    m_cameraMove.SetStats(1.2f,0,0.8f);

    /* Lighting */
    Vec3 arrPos[4];
    arrPos[0] = Vec3(30,30,30);
    arrPos[1] = Vec3(-30,30,-30);
    arrPos[2] = Vec3(30,30,-30);
    arrPos[3] = Vec3(-30,30,30);
    Vec3 arrDir[4];
    arrDir[0] = Vec3(-1,-1,-1);
    arrDir[1] = Vec3(1,-1,1);
    arrDir[2] = Vec3(-1,-1,1);
    arrDir[3] = Vec3(1,-1,-1);
    Vec3 arrCol[4];
    arrCol[0] = Vec3(1,1,1);
    arrCol[1] = Vec3(0,0,1);
    arrCol[2] = Vec3(0,1,0);
    arrCol[3] = Vec3(1,1,0);
    for( int i = 0; i < data.numLights; ++i)
    {
        Light* l = new Light(Color(arrCol[i]*0.8f),/* amb */
            Color(arrCol[i]*0.7f), /* dif */ 
            Color(arrCol[i]*1.2f), /* spec */2.0f, !data.pointLights, data.pointLights, arrDir[i], arrPos[i], 1.1f);
        m_lights.push_back(l);
        if(data.pointLights)
        {
            Material m;
            Color c(arrCol[i]);
            m.SetAmbient(c);
            m.SetSpecular(c);
            m.SetDiffuse(c);
            Sphere* s = new Sphere(arrPos[i], 1.0f, m,false,0.0f, false, 0.0f );
            s->SetColor(c);
            s->MakeLight();
            AddRenderable(s);
        }
    }
}
void Scene::BuildComplexProcedrual(MainRenderStateData& data)
{
    Material m;
    m.SetAmbient(Color(0.8f,0.8f,0.8f,1.0f));
    m.SetDiffuse(Color(0.8f,0.8f,0.8f,1.0f));
    m.SetSpecular(Color(1.0f,1.0f,1.0f,1.0f));

    //-X
    AABBox* box = new AABBox(Vec3(-70,-20,-60), Vec3(-60,20,60), m, true, 2.0f, false, 0.0f);
    box->SetColor(0.2f,0.2f,0.2f);
    AddRenderable(box);

    ///+X
    box = new AABBox(Vec3(60,-20,-60), Vec3(70,20,60), m, true, 2.0f, false, 0.0f);
    box->SetColor(0.2f,0.2f,0.2f);
    AddRenderable(box);

    ///-Z
    box = new AABBox(Vec3(-60,-20,-70), Vec3(60,20,-60), m, true, 2.0f, false, 0.0f);
    box->SetColor(0.2f,0.2f,0.2f);
    AddRenderable(box);

    box = new AABBox(Vec3(-60,-20,60), Vec3(60,20,70), m, true, 2.0f, false, 0.0f);
    box->SetColor(0.2f,0.2f,0.2f);
    AddRenderable(box);

    Sphere* s = new Sphere(Vec3(0,10,0), 20.0f, m, true, 0.8f, false, 0.3f);
    s->SetColor(1,0,0);
    AddRenderable(s);

    s = new Sphere(Vec3(25,10,25), 10.0f, m, false, 0.8f, true, 0.1f);
    s->SetColor(1,1,0);
    AddRenderable(s);

    s = new Sphere(Vec3(-20,20,5), 8.0f, m, true, 0.8f, true, 0.3f);
    s->SetColor(1,1,1);
    AddRenderable(s);

    Plane* p = new Plane(Vec3(0,1,0), -20.0f,m,true,1.0f );
    p->SetColor(0,1,0);
    AddRenderable(p);

    Triangle* t = new Triangle(Vec3(30,0,-30),Vec3(-10,0,-30),
                               Vec3(-10,40,-30),m, false, 0.8f,
                               false, 0.1f);
    t->SetUVs(Vec3(1,0,0),Vec3(0,0,0),Vec3(0,1,0));
    t->SetNormal(Vec3(0,0,1));
    m_triTex = new Texture;
    m_triTex->LoadFromFile("Textures/tri.png");
    t->SetTexture(m_triTex);
    AddRenderable(t);

    //set up the camera stuff
    m_cameraStart = Vec3(30,30,30);
    m_camera->SetPosition(m_cameraStart);
    m_camera->SetLook(0, 0, 0);
    m_camera->SetUp(0,1,0);
    m_camera->ComputeONB();
    m_camera->SetNearFar(0.5f, 1000.0f);
    m_cameraMove.SetStats(1.2f,0,0.8f);

    //lighting
    /* Lighting */
    Vec3 arrPos[4];
    arrPos[0] = Vec3(30,30,30);
    arrPos[1] = Vec3(-30,30,-30);
    arrPos[2] = Vec3(30,30,-30);
    arrPos[3] = Vec3(-30,30,30);
    Vec3 arrDir[4];
    arrDir[0] = Vec3(-1,-1,-1);
    arrDir[1] = Vec3(1,-1,1);
    arrDir[2] = Vec3(-1,-1,1);
    arrDir[3] = Vec3(1,-1,-1);
    Vec3 arrCol[4];
    arrCol[0] = Vec3(1,1,1);
    arrCol[1] = Vec3(0,0,1);
    arrCol[2] = Vec3(0,1,0);
    arrCol[3] = Vec3(1,1,0);
    for( int i = 0; i < data.numLights; ++i)
    {
        Light* l = new Light(Color(arrCol[i]*0.8f),/* amb */
            Color(arrCol[i]*0.7f), /* dif */ 
            Color(arrCol[i]*1.2f), /* spec */2.0f, !data.pointLights, data.pointLights, arrDir[i], arrPos[i], 1.1f);
        m_lights.push_back(l);
        if(data.pointLights)
        {
            Material m;
            Color c(arrCol[i]);
            m.SetAmbient(c);
            m.SetSpecular(c);
            m.SetDiffuse(c);
            Sphere* s = new Sphere(arrPos[i], 1.0f, m,false,0.0f, false, 0.0f );
            s->SetColor(c);
            s->MakeLight();
            AddRenderable(s);
        }
    }
}
void Scene::SetTimer( int which, double val )
{
#ifdef DEBUGGING
    if(which == -1)
        assert(0);
#endif
    m_taskTimers[which] = val;

}


#ifdef DEBUGGING
void Scene::OnMouseButton( const SDL_MouseButtonEvent& Event )
{
    if(Event.state == SDL_RELEASED || Event.button == SDL_BUTTON_LEFT)
        return;

    int x = Event.x;
    int y = m_resY - Event.y;
    std::cout << "Pixel Selected: x: " << x << " , " 
                               <<"y: " << y << "\n";
    std::cout << "Debugging Pixel..." << "\n";

    //Create the debug tracer
    RayTracer* rt = new DebugTracer(this);
    Color	    finalColor;	 	
    Ray			ray;		
    ray.m_origin = (m_camera->GetPos());    		
    m_camera->ComputeDirection(x,y, &ray.m_dir, this);
    //Trace it
    finalColor = rt->TraceRay(ray);
    std::cout << "Final Color =  " << " \n";
    OUT_COLOR(finalColor);
    delete rt;
}
/* Check this ray against the scene */
ContactData Scene::CheckContact_Debug(const Ray& ray, bool nearest) 
{
    ContactData ret(this);		
    float tmin = FLT_MAX;
    m_grid->Hit_Debug(ray, tmin, ret);
    //CheckContactsBruteForce(ret, ray, nearest);
    return ret;
}

#endif
void Scene::ClearBuffers()
{
    //clear the buffers
    memset(m_mainArray, 0, sizeof(float)*(m_resX * m_resY * 3));
    memset(m_depthBuffer, 0, sizeof(float)*(m_resX * m_resY));
}
void Scene::ResetCamera()
{
    m_camera->SetPosition(m_cameraStart);
}

